﻿using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Xml.Linq;

namespace DoNet.Rsa.Framework
{
    /// <summary>
    /// RSA密钥生成类
    /// </summary>
    public class RsaKeyGenerator
    {
        /// <summary>
        /// 生成XML格式的RSA密钥
        /// </summary>
        /// <param name="keySize">密钥大小。单位：比特</param>
        /// <returns>结果：索引0是私钥，索引1是公钥</returns>
        public static List<string> XmlKey(int keySize)
        {
            List<string> res = new List<string>();
            using (RSA rsa = RSA.Create())
            {
                rsa.KeySize = keySize;
                var rsap = rsa.ExportParameters(true);
                var modulus = rsap.Modulus;
                var exponent = rsap.Exponent;
                var p = rsap.P;
                var q = rsap.Q;
                var dp = rsap.DP;
                var dq = rsap.DQ;
                var inverseQ = rsap.InverseQ;
                var d = rsap.D;
                if (modulus != null && exponent != null && p != null && q != null && dp != null && dq != null && inverseQ != null && d != null)
                {
                    XElement privatElement = new XElement("RSAKeyValue");
                    XElement primodulus = new XElement("Modulus", Convert.ToBase64String(modulus));
                    XElement priexponent = new XElement("Exponent", Convert.ToBase64String(exponent));
                    XElement prip = new XElement("P", Convert.ToBase64String(p));
                    XElement priq = new XElement("Q", Convert.ToBase64String(q));
                    XElement pridp = new XElement("DP", Convert.ToBase64String(dp));
                    XElement pridq = new XElement("DQ", Convert.ToBase64String(dq));
                    XElement priinverseQ = new XElement("InverseQ", Convert.ToBase64String(inverseQ));
                    XElement prid = new XElement("D", Convert.ToBase64String(d));

                    privatElement.Add(primodulus);
                    privatElement.Add(priexponent);
                    privatElement.Add(prip);
                    privatElement.Add(priq);
                    privatElement.Add(pridp);
                    privatElement.Add(pridq);
                    privatElement.Add(priinverseQ);
                    privatElement.Add(prid);

                    //添加私钥
                    res.Add(privatElement.ToString());


                    XElement publicElement = new XElement("RSAKeyValue");
                    //Modulus
                    XElement pubmodulus = new XElement("Modulus", Convert.ToBase64String(modulus));
                    //Exponent
                    XElement pubexponent = new XElement("Exponent", Convert.ToBase64String(exponent));

                    publicElement.Add(pubmodulus);
                    publicElement.Add(pubexponent);

                    //添加公钥
                    res.Add(publicElement.ToString());
                }
            }
            return res;
        }

        /// <summary>
        /// 生成Pkcs格式的RSA密钥（Pkcs1/Pkcs8）
        /// </summary>
        /// <param name="keySize">密钥大小。单位：比特</param>
        /// <param name="pkcs">密钥格式 Pkcs1，Pkcs8</param>
        /// <param name="format">是否pem格式 true为是 false为否</param>
        /// <returns>结果：索引0是私钥，索引1是公钥</returns>
        public static List<string> PkcsKey(int keySize, RsaKeyFormat pkcs = RsaKeyFormat.Pkcs1, bool format = true)
        {
            List<string> res = new List<string>();

            IAsymmetricCipherKeyPairGenerator kpGen = GeneratorUtilities.GetKeyPairGenerator("RSA");
            kpGen.Init(new KeyGenerationParameters(new SecureRandom(), keySize));
            var keyPair = kpGen.GenerateKeyPair();

            string privateKey = string.Empty;
            using (StringWriter sw = new StringWriter())
            {
                using (PemWriter pw = new PemWriter(sw))
                {
                    if (pkcs == RsaKeyFormat.Pkcs1)
                    {
                        pw.WriteObject(keyPair.Private);
                    }
                    else if (pkcs == RsaKeyFormat.Pkcs8)
                    {
                        pw.WriteObject(new Pkcs8Generator(keyPair.Private));
                    }
                    pw.Writer.Close();
                }
                privateKey = sw.ToString();
            }

            if (format)
            {
                privateKey = PemFormatHelper.PkcsPrivateKeyFormat(privateKey, pkcs);
            }
            else
            {
                privateKey = PemFormatHelper.PkcsPrivateKeyFormatRemove(privateKey, pkcs);
            }

            res.Add(privateKey);

            string publicKey = string.Empty;
            using (StringWriter sw = new StringWriter())
            {
                using (PemWriter pw = new PemWriter(sw))
                {
                    pw.WriteObject(keyPair.Public);
                    pw.Writer.Close();
                }
                publicKey = sw.ToString();
            }

            if (format)
            {
                publicKey = PemFormatHelper.PublicKeyFormat(publicKey);
            }
            else
            {
                publicKey = PemFormatHelper.PublicKeyFormatRemove(publicKey);
            }

            res.Add(publicKey);

            return res;
        }
    }
}
